
#include <string.h>
#include "pthread_lock.h"

#include "mem_sdram.h"

#include "lvgl.h"
#include "lv_conf.h"

//=================================================宏定义=======================================================//
#define ALIGN_MENT(addr) 								( ( (addr) & 0xFF) ? ( ( (addr) & (~0xFF) ) + 0x100 ) : (addr) )
//字节对齐 eg : addr = ALIGN_MENT(addr);
//( ( (addr) & 0x3) ? ( ( (addr) & (~0x3) ) + 0x4 ) : (addr) )		// 4 	Bytes
//( ( (addr) & 0xFF) ? ( ( (addr) & (~0xFF) ) + 0x100 ) : (addr) )	// 256	Bytes

//================================================全局变量=======================================================//
//因JPEG硬解码器只有一个, 解码视频图像与图片素材会同时需要一个帧缓冲区, 故将此区域复用, 以节省SDRAM空间开销.
#if (LCD_FMT == LCD_RG565)
LV_MEM_ATTR uint8_t JPEG_Decoder_Buff[LCD_HDOT * LCD_VDOT * sizeof(uint16_t)] __attribute__((aligned(4)));

#elif (LCD_FMT == LCD_RGB888)
LV_MEM_ATTR uint8_t JPEG_Decoder_Buff[LCD_HDOT * LCD_VDOT * sizeof(uint32_t)] __attribute__((aligned(4)));

#endif // LCD_FORMAT

#if 1 // 0- LVGL 连续内存管理动态分区实现			1-单一连续内存块管理 (使用用户内存管理可以与 lvgl 内存管理 之间分隔开, 避免用户层访问内存干扰 GUI 运行, 导致 GUI 挂死)

//================================================类型声明=======================================================//
//内存块-头
typedef struct
{
	const char *id; //内存识别码
	size_t size;	//内存块总占用 / Bytes (包括地址对齐偏移量 + 内存头 + 内存数据区)
} mem_header_t;

//线性表-单调栈
typedef struct
{
	void *const data;  //栈的数据基地址
	const size_t size; //栈的数据总容量 / Bytes
	size_t top;		   //栈顶(指向下一块未用内存块地址)
} stack_t;

//================================================本地变量=======================================================//
/* SDRAM 内存管理 :
 * SDRAMM_BASE =>
 *
 * 功能保留区 : 用于特定功能实现, 如不需要某些功能可根据需要删减/复用
 * {
 * 		[LCD_W * LCD_H * 2] 	: LVGL 绘图区, 充当显存 (详见 lvgl_porting/lv_port_disp.c)
 * 		[LCD_W * LCD_H] 		: JPEG 解码缓冲区, 用于全局 JPEG 解码图片(详见 loading/mem_sdram.c)
 * 		[ LV_MEM_SIZE ]  		: LVGL 静态堆 (详见 lv_conf.h line-91)
 * }
 *
 * [ Mem_SDRAM ] :	用户内存管理
 *
 * <= [SDRAMM_BASE + SDRAMM_SIZE]
 */
static LV_MEM_ATTR uint8_t Work_Mem_SDRAM[(SDRAMM_SIZE - (LCD_HDOT * LCD_VDOT * (LV_COLOR_DEPTH >> 3) * 3) - LV_MEM_SIZE)]
	__attribute__((aligned(4)));
//内存头识别码
static const char *const Mem_ID = "Synwit_SWM34S";
//内存操作句柄
static volatile stack_t Mem_SDRAM = {
	.data = (void *)Work_Mem_SDRAM,
	.size = sizeof(Work_Mem_SDRAM) / sizeof(Work_Mem_SDRAM[0]),
	.top = 0
};

//实例化互斥锁
static pthread_mutex_t Mutex_SDRAM = {.lock = false};

//================================================外部接口=======================================================//
/**
 * @brief 	申请一块内存
 * @param 	size : 期望分配的内存块大小
 * @retval	!= NULL - 分配的内存块首地址     NULL - error
 * @note	地址低->高线性增长
 */
void *sdram_malloc(size_t size)
{
	// || 加锁-保护临界段, 避免同时访问竞争产生不可预知的内存错误
	if (Mem_SDRAM.top >= Mem_SDRAM.size || !pthread_mutex_lock(&Mutex_SDRAM))
		return NULL; // assert

	void *ptr = NULL;
	//大小对齐(可选, 方便紧接着的下一块连续内存分配)
	size = ALIGN_MENT(size);

	//地址对齐
	size_t offset_pad = ALIGN_MENT((size_t)&Mem_SDRAM.data[Mem_SDRAM.top]) - (size_t)&Mem_SDRAM.data[Mem_SDRAM.top];
	if (offset_pad < sizeof(mem_header_t)) //地址偏移填充 >= 内存头, 则可重复利用填充空间存放内存头, 否则需要额外对齐内存头
		offset_pad += ALIGN_MENT(sizeof(mem_header_t));

	//计算总占用
	size += offset_pad;

	//剩余容量判定
	if (Mem_SDRAM.top > Mem_SDRAM.size - size)
		goto sdram_malloc_over;

	//压栈-跳过地址偏移
	Mem_SDRAM.top += offset_pad - sizeof(mem_header_t);
	//内存头
	mem_header_t *head = (mem_header_t *)&Mem_SDRAM.data[Mem_SDRAM.top];
	head->id = Mem_ID;
	head->size = size;
	//数据区
	Mem_SDRAM.top += sizeof(mem_header_t);
	ptr = &Mem_SDRAM.data[Mem_SDRAM.top];
	Mem_SDRAM.top += size - offset_pad;

sdram_malloc_over:
	//释放锁
	pthread_mutex_unlock(&Mutex_SDRAM);
	return ptr;
}

/**
 * @brief 	释放一块内存
 * @param 	*ptr : 分配的内存块首地址
 * @retval	\
 * @note	地址高->低线性减少
 */
void sdram_free(void *ptr)
{
	// || 加锁-保护临界段, 避免同时访问竞争产生不可预知的内存错误
	if (ptr == NULL || ((size_t)ptr - (size_t)Mem_SDRAM.data) < sizeof(mem_header_t) || !pthread_mutex_lock(&Mutex_SDRAM))
		return; // assert

	size_t free_size = 0;
	//获取内存块实际占用
	mem_header_t *head = (mem_header_t *)((size_t)ptr - sizeof(mem_header_t));
	if (head->id == Mem_ID)
		free_size = head->size;

	//清除ID/Size(可选)
//	memset(head, 0xFF, sizeof(mem_header_t));

	if (Mem_SDRAM.top < free_size)
		free_size = Mem_SDRAM.top; //栈顶指向为下一块未用内存块地址, 若为 0 则表示空栈

	//出栈
	Mem_SDRAM.top -= free_size;

	//立即清理脏值(可选)
//	memset((void *)&Mem_SDRAM.data[Mem_SDRAM.top], 0xFF, free_size);

	//解锁
	pthread_mutex_unlock(&Mutex_SDRAM);
}

/**
 * @brief 	获取空闲可用内存大小
 * @param 	/
 * @retval	剩余内存大小
 * @note	/
 */
size_t sdram_get_idle_size(void)
{
	return Mem_SDRAM.size - Mem_SDRAM.top;
}

/**
 * @brief 	获取栈顶
 * @param 	/
 * @retval	栈顶指针(指向下一块未用内存块地址)
 * @note	/
 */
void *sdram_get_top(void)
{
	return &Mem_SDRAM.data[Mem_SDRAM.top];
}

#else

/**
 * @brief 	申请一块内存
 * @param 	size : 期望分配的内存块大小
 * @retval	!= NULL - 分配的内存块首地址     NULL - error
 * @note	\
 */
void *sdram_malloc(size_t size)
{
	size = ALIGN_MENT(size);
	// lvgl 暂未提供内存对齐 api , 需要等到 V9 版本

#if 1 // malloc
	return lv_mem_alloc(size);

#else // calloc
	void *ptr = lv_mem_alloc(size);
	memset(ptr, 0, size);
	return ptr;

#endif
}

/**
 * @brief 	释放一块内存
 * @param 	ptr : 分配的内存块首地址
 * @retval	\
 * @note	\
 */
void sdram_free(void *ptr)
{
	lv_mem_free(ptr);
}

/**
 * @brief 	获取空闲可用内存大小
 * @param 	/
 * @retval	剩余内存大小
 * @note	/
 */
size_t sdram_get_idle_size(void)
{
	lv_mem_monitor_t mem_monitor;
	lv_mem_monitor(&mem_monitor);
	return mem_monitor.free_size;
}

#endif
